package org.github.xhjcehust.dubbo.proxy.controller;

import com.alibaba.fastjson.JSONObject;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.service.GenericService;
import org.github.xhjcehust.dubbo.proxy.common.CommonTools;
import org.github.xhjcehust.dubbo.proxy.model.DubboInvokerParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author xiaohengjin<xiaohengjin@corp.netease.com>
 * @date 2019/7/26
 */
@Controller
@RequestMapping("/dubboInvoker")
public class DubboInvoker {

    @Value("${dubbo.registry.address}")
    private String registryAddress;

    @Value("${application.invoker.name}")
    private String appName;

    @Value("${dubbo.registry.version}")
    private String version;


    /**
     * 一般业务
     * @param param
     * @return
     */
    @RequestMapping("/index")
    @ResponseBody
    public Object index(@RequestBody(required = false) DubboInvokerParam param) {
        String msg = CommonTools.check(param);
        if(msg != null){
            return msg;
        }

        return doInvoke(param);
    }

    /**
     * 适配一般dubbo业务
     * @param param
     * @return
     */
    private Object doInvoke(DubboInvokerParam param) {

        ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
        reference.setApplication(new ApplicationConfig(appName));

        reference.setInterface(param.getInterfaceName());
        reference.setVersion(version);
        reference.setGeneric(true);
        reference.setGroup(param.getGroup());
        reference.setId(param.getId());
//        reference.setUrl(param.getUrl());
        reference.setTimeout(60000);
        reference.setRetries(0);

        RegistryConfig registry = new RegistryConfig();
        registry.setAddress(registryAddress);
        reference.setRegistry(registry);

        GenericService genericService = reference.get();

        Map<String, String> attachments = param.getAttachments();
        if (attachments != null) {
            RpcContext.getContext().setAttachments(attachments);
        }

        String [] argTypes = new String[param.getArgTypes().length];
        Object [] argObjects = new Object[param.getArgObjects().length];
        try {
            for(int i=0,l=param.getArgTypes().length;i<l;i++){
                String argType = param.getArgTypes()[i];


                if(param.getArgObjects()[i] instanceof LinkedHashMap){
                    LinkedHashMap<String,Object> argObject = (LinkedHashMap) param.getArgObjects()[i];
                    if(argType.contains("<")&&argType.contains(">")){//存在泛型
                        System.err.println("适配泛型---开始");

                        String parentArgType = argType.substring(0,argType.indexOf("<"));
                        argTypes[i] = parentArgType;
                        String childArgType = argType.substring(argType.indexOf("<")+1,argType.indexOf(">"));

                        Class clsParent = Class.forName(parentArgType);
                        Object objParent = JSONObject.parseObject(JSONObject.toJSONString(argObject), clsParent);
                        for (Map.Entry<String,Object> entry : argObject.entrySet()) {
                            Class clsChild = Class.forName(childArgType);
                            Object objChild = JSONObject.parseObject(JSONObject.toJSONString(entry.getValue()), clsChild);
                            Method[] methods = clsParent.getDeclaredMethods();
                            for(Method method : methods){
                                if(method.getName().toLowerCase().contains(("set"+entry.getKey()).toLowerCase())){
                                    method.invoke(objParent,objChild);
                                    argObjects[i] = objParent;
                                }
                            }
                        }
                        System.err.println("适配泛型---成功");
                    }else if(argType.contains("java.lang")||argType.contains("java.util")){ //普通类型
                        argTypes[i] = argType;
                        Iterator iterator =  argObject.values().iterator();
                        argObjects[i] = iterator.next();
                    }else{
                        System.err.println("适配普通类型---开始");
                        argTypes[i] = argType;
                        if(argObject!=null){
                            if(JSONObject.parseObject(JSONObject.toJSONString(argObject)).size()>0){
                                Class cls = Class.forName(argType);
                                Object obj = JSONObject.parseObject(JSONObject.toJSONString(argObject), cls);
                                argObjects[i] = obj;
                            }else{
                                argObjects[i] = null;
                            }
                        }else{
                            argObjects[i] = null;
                        }
                        System.err.println("适配普通类型---成功");
                    }
                }else{
                    argObjects[i] = param.getArgObjects()[i];
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.err.println("开始执行");
        long t = System.currentTimeMillis();
        Object result = genericService.$invoke(param.getMethodName(), argTypes, argObjects);
        System.out.println("耗时："+(System.currentTimeMillis()-t)+"   ["+result+"]");
        return result;
    }
}
